# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2022/6/11 17:12
# @Author : xyjiang1216
# @Email : jiangxuyan@nudt.edu.cn
# @File : fwd_tbl_linker.py
# @Version: planner_builder 1.0
from typing import List, Dict, Union
import xml.etree.ElementTree as EleTree
from tsn_builder_struct.fwd_tbl_structure import FwdTbl, FwdTblEachNode, FwdEntry_union, FwdEntry
from lib.tsn_builder_lib import host_to_net
from tsn_builder_linker.reg_addr_parser import RegAddr


def link_fwd_tbl(node: dict,
                 fwd_tbl: FwdTbl,
                 reg_addr: RegAddr) -> List[Dict[str, Union[str, List[str]]]]:
    # 找到该节点对应的转发表
    node_id = node['src_node']
    tbl_each_node = []
    for tbl in fwd_tbl.tbl_set:
        if tbl.node_id == node_id:
            tbl_each_node = tbl.tbl_each_node
            # [print(entry) for entry in tbl_each_node]
            break

    if not tbl_each_node:
        return []

    # 处理该节点的转发表的每一条表项
    # 硬件将flow_id作为索引查表，因此flow_id与转发表寄存器基地址
    # 之间存在映射关系
    dic = []  # 返回值
    for entry in tbl_each_node:
        flow_id = entry.flow_id
        # 如果flow_id不为空，说明
        # 该流在该节点有转发表表项
        if flow_id is not None:
            fwd_entry = entry.fwd_entry
            value1 = fwd_entry.byte.entry1
            value2 = fwd_entry.byte.entry2
            value1_str = host_to_net(value1)
            value2_str = host_to_net(value2)
            # value1_str = hex(value1)[2:]
            # value2_str = hex(value2)[2:]

            # 计算该转发表的地址
            addr = reg_addr.fwd_tbl['addr']
            width = reg_addr.fwd_tbl['width']
            addr = addr + flow_id * width
            addr_str = host_to_net(addr)
            addr_value = {
                'addr': addr_str,
                'value': [value1_str, value2_str]
            }
            dic.append(addr_value)
    return dic


def _get_fwd_tbl(fwd_tbl_xml):
    fwd_entry_set = []
    root = EleTree.parse(fwd_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_entry = {}
        node_id = node_ele.findtext('node_id')
        node_entry.update({'node_id': int(node_id)})
        entry_ele_set = node_ele.findall('entry')
        node_entry.update({'entry_set': []})
        for entry_ele in entry_ele_set:
            flow_id = entry_ele.findtext('flow_id')
            port_id = entry_ele.findtext('outport')
            priority = entry_ele.findtext('priority')
            reserve = entry_ele.findtext('reserve')
            flow_period = entry_ele.findtext('flow_period')
            inject_time_slot = entry_ele.findtext('inject_time_slot')
            entry = {
                'flow_id': int(flow_id),
                'outport': int(port_id, 16),
                'priority': int(priority),
                'reserve': int(reserve),
                'flow_period': int(flow_period),
                'inject_time_slot': int(inject_time_slot)
            }
            node_entry['entry_set'].append(entry)
        fwd_entry_set.append(node_entry)
    return fwd_entry_set


def _init_fwd_tbl_obj(fwd_set):
    fwd_tbl = FwdTbl()
    for tbl_each_node in fwd_set:
        node_id = tbl_each_node['node_id']
        entry_set = tbl_each_node['entry_set']
        fwd_tbl_each_node = FwdTblEachNode(node_id)
        for entry in entry_set:
            flow_id = entry['flow_id']
            fwd_entry_union = FwdEntry_union()
            outport = entry['outport']
            fwd_entry_union.bit.outport = outport
            priority = entry['priority']
            fwd_entry_union.bit.priority = priority
            reserve = entry['reserve']
            fwd_entry_union.bit.reserve = reserve
            flow_period = entry['flow_period']
            fwd_entry_union.bit.flow_period = flow_period
            inject_time_slot = entry['inject_time_slot']
            fwd_entry_union.bit.inject_time_slot = inject_time_slot

            fwd_entry = FwdEntry(flow_id,
                                 fwd_entry_union)
            fwd_tbl_each_node.append_entry(fwd_entry)
        fwd_tbl.append_tbl(fwd_tbl_each_node)
    return fwd_tbl


def read_fwd_tbl(fwd_tbl_xml):
    """
    从fwd_tbl.xml中获取所有节点的xml信息
    :param fwd_tbl_xml:
    :return:
    """

    # 从fwd_tbl_xml中读取fwd_entry_set
    fwd_entry_set = _get_fwd_tbl(fwd_tbl_xml)

    # 初始化FwdTbl类的对象
    fwd_tbl = _init_fwd_tbl_obj(fwd_entry_set)

    return fwd_tbl


def main():
    fwd_tbl_xml = '../output_xml/tbl_xml/fwd_tbl.xml'
    fwd_tbl = read_fwd_tbl(fwd_tbl_xml)



if __name__ == '__main__':
    main()
